<template>
	<Form
		v-bind="getBindValue"
		:class="getFormClass"
		ref="formElRef"
		:model="formModel"
		@keypress.enter="handleEnterPress"
	>
		<Row v-bind="getRow">
			<slot name="formHeader"></slot>
			<template v-for="schema in getSchema" :key="schema.field">
				<FormItem
					:tableAction="tableAction"
					:formActionType="formActionType"
					:schema="schema"
					:formProps="getProps"
					:allDefaultValues="defaultValueRef"
					:formModel="formModel"
					:setFormModel="setFormModel"
				>
					<template #[item]="data" v-for="item in Object.keys($slots)">
						<slot :name="item" v-bind="data || {}"></slot>
					</template>
				</FormItem>
			</template>

			<FormAction v-bind="getFormActionBindProps">
				<template
					#[item]="data"
					v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
				>
					<slot :name="item" v-bind="data || {}"></slot>
				</template>
			</FormAction>
			<slot name="formFooter"></slot>
		</Row>
	</Form>
</template>
<script lang="ts">
import type { FormActionType, FormProps, FormSchema } from './types/form';
import type { AdvanceState } from './types/hooks';
import { mergeProps, Ref } from 'vue';

import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
import { Form, Row } from 'ant-design-vue';
import FormItem from './components/FormItem.vue';
import FormAction from './components/FormAction.vue';

import { dateItemType } from './helper';
import { dateUtil } from '@/utils/dateUtil';

import { deepMerge } from '@/utils';

import { useFormValues } from './hooks/useFormValues';
import { useFormEvents } from './hooks/useFormEvents';
import { createFormContext } from './hooks/useFormContext';
import { useAutoFocus } from './hooks/useAutoFocus';

import { basicFormProps } from './props';
import { useDesign } from '@/hooks/web/useDesign';
import { useModalContext } from '#/Modal';

export default defineComponent({
	name: 'BasicForm',
	components: { FormItem, Form, Row, FormAction },
	props: basicFormProps,
	emits: ['advanced-change', 'reset', 'submit', 'register'],
	setup(props, { emit, attrs }) {
		const formModel = reactive<Recordable>({});
		const modalFn = useModalContext();

		const advanceState = reactive<AdvanceState>({
			isAdvanced: true,
			hideAdvanceBtn: false,
			isLoad: false,
			actionSpan: 6,
		});

		const defaultValueRef = ref<Recordable>({});
		const isInitedDefaultRef = ref(false);
		const propsRef = ref<Partial<FormProps>>({});
		const schemaRef = ref<Nullable<FormSchema[]>>(null);
		const formElRef = ref<Nullable<FormActionType>>(null);

		const { prefixCls } = useDesign('basic-form');

		const getProps = computed((): FormProps => {
			// return { ...props, ...unref(propsRef) } as FormProps;
			return mergeProps(props, unref(propsRef)) as FormProps;
		});

		const getFormClass = computed(() => {
			return [
				prefixCls,
				{
					[`${prefixCls}--compact`]: unref(getProps).compact,
				},
			];
		});

		const getRow = computed((): Recordable => {
			const { baseRowStyle = {}, rowProps } = unref(getProps);
			return {
				style: baseRowStyle,
				...rowProps,
			};
		});

		const getBindValue = computed(() => ({ ...attrs, ...props, ...unref(getProps) } as Recordable));

		const getSchema = computed((): FormSchema[] => {
			const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
			for (const schema of schemas) {
				const { defaultValue, component } = schema;
				// handle date type
				if (defaultValue && dateItemType.includes(component)) {
					if (!Array.isArray(defaultValue)) {
						schema.defaultValue = dateUtil(defaultValue);
					} else {
						const def: any[] = [];
						defaultValue.forEach(item => {
							def.push(dateUtil(item));
						});
						schema.defaultValue = def;
					}
				}
			}
			if (unref(getProps).showAdvancedButton) {
				return schemas.filter(schema => schema.component !== 'Divider') as FormSchema[];
			} else {
				return schemas as FormSchema[];
			}
		});

		// TODO: 折叠多余行
		// const { handleToggleAdvanced } = useAdvanced({
		// 	advanceState,
		// 	emit,
		// 	getProps,
		// 	getSchema,
		// 	formModel,
		// 	defaultValueRef,
		// });

		const { handleFormValues, initDefault } = useFormValues({
			getProps,
			defaultValueRef,
			getSchema,
			formModel,
		});

		useAutoFocus({
			getSchema,
			getProps,
			isInitedDefault: isInitedDefaultRef,
			formElRef: formElRef as Ref<FormActionType>,
		});

		const {
			handleSubmit,
			setFieldsValue,
			clearValidate,
			validate,
			validateFields,
			getFieldValue,
			getFieldsValue,
			updateSchema,
			resetSchema,
			appendSchemaByField,
			removeSchemaByFiled,
			resetFields,
			scrollToField,
		} = useFormEvents({
			emit,
			attrs,
			getProps,
			formModel,
			getSchema,
			defaultValueRef,
			formElRef: formElRef as Ref<FormActionType>,
			schemaRef: schemaRef as Ref<FormSchema[]>,
			handleFormValues,
		});

		createFormContext({
			resetAction: resetFields,
			submitAction: handleSubmit,
		});

		watch(
			() => unref(getProps).model,
			() => {
				const { model } = unref(getProps);
				if (!model) return;
				setFieldsValue(model);
			},
			{
				immediate: true,
			},
		);

		watch(
			() => unref(getProps).schemas,
			schemas => {
				resetSchema(schemas ?? []);
			},
		);

		watch(
			() => getSchema.value,
			schema => {
				nextTick(() => {
					modalFn?.redoModalHeight?.();
				});
				if (unref(isInitedDefaultRef)) {
					return;
				}
				if (schema?.length) {
					initDefault();
					isInitedDefaultRef.value = true;
				}
			},
		);

		async function setProps(formProps: Partial<FormProps>): Promise<void> {
			propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
		}

		function setFormModel(key: string, value: any) {
			formModel[key] = value;

			const { validateTrigger } = unref(getBindValue);
			if (!validateTrigger || validateTrigger === 'change') {
				validateFields([key]).catch(_err => {});
			}
		}

		function handleEnterPress(e: KeyboardEvent) {
			const { autoSubmitOnEnter } = unref(getProps);
			if (!autoSubmitOnEnter) return;
			if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
				const target: HTMLElement = e.target as HTMLElement;
				if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
					handleSubmit();
				}
			}
		}

		const formActionType: Partial<FormActionType> = {
			getFieldValue,
			getFieldsValue,
			setFieldsValue,
			resetFields,
			updateSchema,
			resetSchema,
			setProps,
			removeSchemaByFiled,
			appendSchemaByField,
			clearValidate,
			validateFields,
			validate,
			submit: handleSubmit,
			scrollToField: scrollToField,
		};

		onMounted(() => {
			initDefault();
			emit('register', formActionType);
		});

		return {
			prefixCls,
			getBindValue,
			handleEnterPress,
			formModel,
			defaultValueRef,
			advanceState,
			getRow,
			getProps,
			formElRef,
			getSchema,
			formActionType: formActionType as any,
			setFormModel,
			getFormClass,
			getFormActionBindProps: computed((): Recordable => ({ ...getProps.value, ...advanceState })),
			...formActionType,
		};
	},
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-form';

.@{prefix-cls} {
	.ant-form-item {
		&-label label::after {
			margin: 0 6px 0 2px;
		}

		&-with-help {
			margin-bottom: 0;
		}

		&:not(.ant-form-item-with-help) {
			margin-bottom: 20px;
		}

		&.suffix-item {
			.ant-form-item-children {
				display: flex;
			}

			.ant-form-item-control {
				margin-top: 4px;
			}

			.suffix {
				display: inline-flex;
				padding-left: 6px;
				margin-top: 1px;
				line-height: 1;
				align-items: center;
			}
		}
	}

	.ant-form-explain {
		font-size: 14px;
	}

	&--compact {
		.ant-form-item {
			margin-bottom: 8px !important;
		}
	}
}
</style>
